#include "bib_Disco.h"

extern struct EST_DISCO disco;
extern struct EST_CHS poscicionCabezal;

extern struct EST_PEDIDOS *primerPedido;
extern struct EST_PEDIDOS *ultimoPedido;

extern pthread_mutex_t mutexCola;
extern pthread_mutex_t mutexSocket;

extern char modoInicio;
extern int sockConsola;

extern char *persistencia;
extern uint32_t cantEscrituras;
extern size_t tamanio;

int tamanioCola(uint16_t *tamanio);

uint16_t distancia(uint16_t nro1, uint16_t nro2){

	if (nro1 < nro2)
		return (nro2 - nro1);
	else
		return (nro1 - nro2);
}
/*=====================================================================================*/
int deFisicaALogica(uint32_t *direccionLogica, struct EST_CHS chs){

	*direccionLogica = disco.chs.sector * chs.cilindro + chs.sector;
	
	return FIN_OK;
}
/*=====================================================================================*/
int deLogicaAFisica(uint32_t direccionLogica, struct EST_CHS *chs){

	chs->cilindro = direccionLogica / disco.chs.sector;
	chs->sector = direccionLogica % disco.chs.sector;
	
	return FIN_OK;
}
/*=====================================================================================*/
int agregarPedido(uint32_t id, char tipo, uint32_t sector, int sock, char *buffer){

	struct EST_PEDIDOS *ptr;
	struct EST_CHS direccionFisica;
	char cadena[30];

	if (!(ptr = (struct EST_PEDIDOS *) malloc(sizeof(struct EST_PEDIDOS))))
		return FIN_ERROR;
		
	ptr->id = id;
	ptr->tipo = tipo;
	ptr->sector = sector;
	ptr->sock = sock;
	ptr->buffer = buffer;
	ptr->siguiente = NULL;
	
	if (primerPedido == NULL)
		primerPedido = ptr;
	else
		ultimoPedido->siguiente = ptr;
	ultimoPedido = ptr;

	deLogicaAFisica(sector, &direccionFisica);
	sprintf(cadena, "Sector %d:%d pedido para", direccionFisica.cilindro, direccionFisica.sector);
	if (tipo == LECTURA)
		imprimirLog(DETALLE, cadena, "lectura");
	if (tipo == ESCRITURA)
		imprimirLog(DETALLE, cadena, "escritura");
	if (tipo == PRUEBA)
		imprimirLog(DETALLE, cadena, "trace");

	return FIN_OK;
}
/*=====================================================================================*/
int sacarPedido(struct EST_PEDIDOS *pedidoAnterior, uint32_t *id, char *tipo, uint32_t *sector, int *sock, char **buffer){
	
	struct EST_PEDIDOS *ptr;
	
	if (pedidoAnterior)
		ptr = pedidoAnterior->siguiente;
	else
		ptr = primerPedido;	
	
	*id = ptr->id;
	*tipo = ptr->tipo;
	*sector = ptr->sector;
	*sock = ptr->sock;
	*buffer = ptr->buffer;
	
	if (pedidoAnterior)
		pedidoAnterior->siguiente = ptr->siguiente;
	else
		primerPedido = ptr->siguiente;

	if (ptr == ultimoPedido)
		ultimoPedido = pedidoAnterior;

	if (primerPedido == NULL)
		ultimoPedido = NULL;
	
	free(ptr);
	
	return FIN_OK;
}
/*=====================================================================================*/
int sacarPedidoSSTF(struct EST_PEDIDOS **pedidoSeleccionado){
	
	struct EST_PEDIDOS *actual;
	struct EST_PEDIDOS *anterior;
	/*struct EST_PEDIDOS *pedidoSeleccionado;*/
	struct EST_CHS chsActual;
	unsigned int distanciaActual;
	unsigned int distanciaAuxiliar;
	
	anterior = primerPedido;
	actual = primerPedido->siguiente;
	
	*pedidoSeleccionado = NULL;
	deLogicaAFisica(anterior->sector, &chsActual);
	
	distanciaActual = distancia(chsActual.cilindro, poscicionCabezal.cilindro);

	while(actual){
		deLogicaAFisica(actual->sector, &chsActual);
		distanciaAuxiliar = distancia(chsActual.cilindro, poscicionCabezal.cilindro);

		if (distanciaAuxiliar < distanciaActual){
			*pedidoSeleccionado = anterior;
			distanciaActual = distanciaAuxiliar;
		}
		
		anterior = actual;
		actual = actual->siguiente;
	}
	
	return FIN_OK;
}
/*=====================================================================================*/
int sacarPedidoCLOOK(struct EST_PEDIDOS **pedidoSeleccionado){
	
	struct EST_PEDIDOS *actual;
	struct EST_PEDIDOS *anterior;
	/*struct EST_PEDIDOS *pedidoSeleccionado;*/
	struct EST_CHS chsActual;
	uint16_t topeMinimo;
	uint16_t topeMaximo;
	
	topeMinimo = poscicionCabezal.cilindro;
	topeMaximo = disco.chs.cilindro;
	
	anterior = NULL;
	actual = primerPedido;
	
	do{
		deLogicaAFisica(actual->sector, &chsActual);
		
		if ((chsActual.cilindro >= topeMinimo) && (chsActual.cilindro < topeMaximo)){
			topeMaximo = chsActual.cilindro;
			*pedidoSeleccionado = anterior;
		}
		
		anterior = actual;
		actual = actual->siguiente;
		
	} while(actual);
	
	if (topeMaximo == disco.chs.cilindro){
		
		topeMinimo = 0;
		topeMaximo = poscicionCabezal.cilindro;
	
		anterior = NULL;
		actual = primerPedido;
	
		do{
			deLogicaAFisica(actual->sector, &chsActual);
		
			if ((chsActual.cilindro >= topeMinimo) && (chsActual.cilindro < topeMaximo)){
				topeMaximo = chsActual.cilindro;
				*pedidoSeleccionado = anterior;
			}
		
			anterior = actual;
			actual = actual->siguiente;
		
		} while(actual);
	}
	
	return FIN_OK;
}
/*=====================================================================================*/
int seekTime(uint16_t cilindroDestino, unsigned long *tiempoConsumido, struct EST_CHS *poscFinal){

	/*unsigned long cantSectoresRecorridos;*/

	*tiempoConsumido = distancia(poscicionCabezal.cilindro, cilindroDestino) * disco.tiempoEntrePistas;
	
	*poscFinal = poscicionCabezal;
	poscFinal->cilindro = cilindroDestino;

	/*cantSectoresRecorridos = *tiempoConsumido / disco.tiempoEntreSectores;
	if (*tiempoConsumido % disco.tiempoEntreSectores)
		cantSectoresRecorridos++;
	
	poscFinal->sector = poscicionCabezal.sector + cantSectoresRecorridos;
	poscFinal->sector = poscFinal->sector % disco.chs.sector;*/
	
	return FIN_OK;
}
/*=====================================================================================*/
int recorridoSeekTime(uint16_t cilindroDestino, unsigned long *tiempoConsumido, uint32_t **sectoresRecorridos, uint16_t *cantPistasRecorridas){

	uint16_t i;
	struct EST_CHS poscFinal;
	uint32_t dirLogica;
	int8_t sentido;

	*cantPistasRecorridas = distancia(poscicionCabezal.cilindro, cilindroDestino);

	if (*cantPistasRecorridas){
		*sectoresRecorridos = (uint32_t *) malloc(*cantPistasRecorridas * sizeof(uint32_t));

		if(poscicionCabezal.cilindro < cilindroDestino)
			sentido = 1;
		else
			sentido = -1;

		for(i = 0; i < *cantPistasRecorridas; i++){
			seekTime(poscicionCabezal.cilindro + sentido * (i + 1), tiempoConsumido, &poscFinal);
			deFisicaALogica(&dirLogica, poscFinal);
			*((*sectoresRecorridos) + i) = dirLogica;
		}

		poscicionCabezal = poscFinal;
	}
	else{
		*tiempoConsumido = 0;
		*sectoresRecorridos = NULL;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int rotationalDelay(uint16_t sectorDestino, double *tiempoConsumido){
	
	uint16_t cantSectoresRecorridos;
	
	if (poscicionCabezal.sector > sectorDestino)
		cantSectoresRecorridos = disco.chs.sector + sectorDestino - poscicionCabezal.sector;
	else
		cantSectoresRecorridos = sectorDestino - poscicionCabezal.sector;
		
	*tiempoConsumido = cantSectoresRecorridos * disco.tiempoEntreSectores;
	
	poscicionCabezal.sector = sectorDestino;
	
	return FIN_OK;
}
/*=====================================================================================*/
int recorridoRotationalDelay(uint16_t sectorDestino, double *tiempoConsumido, uint32_t **sectoresRecorridos, uint16_t *cantSectoresRecorridos){

	uint16_t i;
	uint16_t cantPistasRecorridas;
	struct EST_CHS dirFisica;
	uint32_t dirLogica;

	cantPistasRecorridas = *cantSectoresRecorridos;

	if (sectorDestino > poscicionCabezal.sector)
		*cantSectoresRecorridos += sectorDestino - poscicionCabezal.sector;
	else if (sectorDestino < poscicionCabezal.sector)
		*cantSectoresRecorridos += disco.chs.sector + sectorDestino - poscicionCabezal.sector;

	if (sectorDestino != poscicionCabezal.sector){
		if (cantPistasRecorridas)
			*sectoresRecorridos = (uint32_t *) realloc(*sectoresRecorridos, *cantSectoresRecorridos * sizeof(uint32_t));
		else
			*sectoresRecorridos = (uint32_t *) malloc(*cantSectoresRecorridos * sizeof(uint32_t));

		dirFisica = poscicionCabezal;

		/*for (i = cantPistasRecorridas; i < *cantSectoresRecorridos; i++){
			dirFisica.sector += i + 1;
			dirFisica.sector = dirFisica.sector % disco.chs.sector;
			deFisicaALogica(&dirLogica, dirFisica);
			*((*sectoresRecorridos) + i) = dirLogica;
		}*/
		for (i = 0; i < (*cantSectoresRecorridos - cantPistasRecorridas); i++){
			dirFisica.sector++;
			dirFisica.sector = dirFisica.sector % disco.chs.sector;
			deFisicaALogica(&dirLogica, dirFisica);
			*((*sectoresRecorridos) + i + cantPistasRecorridas) = dirLogica;
		}

		rotationalDelay(sectorDestino, tiempoConsumido);
	}
	else
		*tiempoConsumido = 0;

	return FIN_OK;
}
/*=====================================================================================*/
int tamanioCola(uint16_t *tamanio){

	struct EST_PEDIDOS *ptr;

	ptr = primerPedido;
	*tamanio = 0;

	/*if (poscicionCabezal.sector >= disco.chs.sector)
		poscicionCabezal.sector -= disco.chs.sector;*/
	while(ptr){
		(*tamanio)++;
		ptr = ptr->siguiente;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int obtenerInfoPlanificacion(struct EST_PLANIFICACION *planif, uint32_t **sectores){

	struct EST_PEDIDOS *ptr;
	int i;

	/*planif->cantSectoresDisco = disco.chs.sector;*/
	tamanioCola(&(planif->tamanioCola));
	deFisicaALogica(&(planif->posicionActual), poscicionCabezal);

	if(planif->tamanioCola){
		*sectores = (uint32_t *) malloc(planif->tamanioCola * sizeof(uint32_t));

		ptr = primerPedido;

		for(i = 0; ptr; i++, ptr = ptr->siguiente)
			memcpy(*sectores + i/* * sizeof(uint32_t)*/, (char *) &(ptr->sector), sizeof(uint32_t));
	}

	return FIN_OK;
}
/*=====================================================================================*/
int atenderPedido(void){
	
	uint32_t id;
	char tipo;
	int sock;
	char *buffer;
	struct EST_CHS direccionFisica;
	unsigned long tiempoBusqueda;
	double tiempoGiro;
	struct HEADER_NIPC header;
	struct EST_PLANIFICACION planif;
	uint32_t *sectoresCola;
	uint32_t *sectoresRecorridos;
	char *cadena;
	char cadenaAux[50];
	uint16_t i;
	/*uint16_t len;*/
	struct EST_PEDIDOS *pedidoSeleccionado;

	pthread_mutex_lock(&mutexCola);
	obtenerInfoPlanificacion(&planif, &sectoresCola);

	if (disco.planificador == SSTF)
		sacarPedidoSSTF(&pedidoSeleccionado);
	else
		sacarPedidoCLOOK(&pedidoSeleccionado);
	sacarPedido(pedidoSeleccionado, &id, &tipo, &(planif.sectorSolicitado), &sock, &buffer);

	if (planif.tamanioCola > 1){
		if (disco.planificador == SSTF)
			sacarPedidoSSTF(&pedidoSeleccionado);
		else
			sacarPedidoCLOOK(&pedidoSeleccionado);

		if (pedidoSeleccionado)
			planif.proximoSector = pedidoSeleccionado->siguiente->sector;
		else
			planif.proximoSector = primerPedido->sector;
	}
	else
		planif.proximoSector = 0;

		pthread_mutex_unlock(&mutexCola);

	deLogicaAFisica(planif.sectorSolicitado, &direccionFisica);
	
	if ((disco.planificador == CLOOK) && (poscicionCabezal.cilindro > direccionFisica.cilindro))
		poscicionCabezal.cilindro = direccionFisica.cilindro;
		
	recorridoSeekTime(direccionFisica.cilindro, &tiempoBusqueda, &sectoresRecorridos, &planif.cantSectoresRecorridos);
	/*seekTime(direccionFisica.cilindro, &tiempoBusqueda);*/
	recorridoRotationalDelay(direccionFisica.sector, &tiempoGiro, &sectoresRecorridos, &planif.cantSectoresRecorridos);
	/*rotationalDelay(direccionFisica.sector, &tiempoGiro);*/
	planif.tiempoConsumido = tiempoBusqueda + tiempoGiro;
	
	if (tipo == LECTURA){
		struct PAYLOAD_RESPUESTA_LECTURA respuesta;

		planif.tiempoConsumido += disco.tiempoLectura;
		if (disco.tiempoEscritura)
			usleep((unsigned) (disco.tiempoLectura * 1000));

		header.type = tipo;
		header.payloadLength = sizeof(PAYLOAD_RESPUESTA_LECTURA);
		respuesta.id = id;
		memcpy(respuesta.buffer, persistencia + TAM_SECTOR * planif.sectorSolicitado, TAM_SECTOR);

		pthread_mutex_lock(&mutexSocket);
		if (enviarMensajeNIPC(sock, &header, (char *) &respuesta)){
			pthread_mutex_unlock(&mutexSocket);
			imprimirLogNumerico(ERROR, "No se pudo responder al pedido:", id);
			if (planif.tamanioCola)
				free(sectoresCola);
			return FIN_ERROR;
		}
		pthread_mutex_unlock(&mutexSocket);

		if (modoInicio == LISTEN)
			cerrarSocket(&sock);

		/*Loguear*/
	}

	else if (tipo == ESCRITURA){
		planif.tiempoConsumido += disco.tiempoEscritura;

		if (disco.tiempoEscritura)
			usleep((unsigned) (disco.tiempoEscritura * 1000));
		/*Escribir*/

		memcpy(persistencia + TAM_SECTOR * planif.sectorSolicitado, buffer, TAM_SECTOR);

		if (cantEscrituras == 32000){
			if ((msync(persistencia, tamanio, MS_SYNC)) == -1)
				imprimirLog(ERROR, "msync", "");
			cantEscrituras = 0;
		}
		else
			cantEscrituras++;

		header.type = tipo;
		header.payloadLength = sizeof(uint32_t);

		/*cadena = (char *) malloc(12);
		deLogicaAFisica(planif.sectorSolicitado, &direccionFisica);
		sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
		imprimirLog(DEBUG,"","");
		imprimirLogNumerico(INFO, "Tamanio de cola de pedidos:", planif.tamanioCola);
		imprimirLog(INFO, "Sector solicitado:", cadena);
		imprimirLog(DEBUG,"","");
		free(cadena);*/

		pthread_mutex_lock(&mutexSocket);
		if (enviarMensajeNIPC(sock, &header, (char *) &id)){
			pthread_mutex_unlock(&mutexSocket);
			imprimirLogNumerico(ERROR, "No se pudo responder al pedido:", id);
			if (planif.tamanioCola)
				free(sectoresCola);
			return FIN_ERROR;
		}
		pthread_mutex_unlock(&mutexSocket);

		free(buffer);
		if (!(modoInicio == CONNECT || sock == sockConsola))
			cerrarSocket(&sock);
	}

	else if (tipo == PRUEBA){
		char *rtaConsola;
		/*int len = sizeof(struct EST_PLANIFICACION) + sizeof(uint32_t) * planif.tamanioCola;*/

		header.type = tipo;
		header.payloadLength = sizeof(struct EST_PLANIFICACION) + sizeof(uint32_t) * planif.cantSectoresRecorridos;

		rtaConsola = (char *) malloc(header.payloadLength);
		memcpy(rtaConsola, (char *) &planif, sizeof(struct EST_PLANIFICACION));
		if (planif.cantSectoresRecorridos)
			memcpy(rtaConsola + sizeof(struct EST_PLANIFICACION), (char *) sectoresRecorridos, sizeof(uint32_t) * planif.cantSectoresRecorridos);

		if (enviarMensajeNIPC(sock, &header, rtaConsola)){
			imprimirLog(ERROR, "No se pudo responder a la consola el trace", "");
			if (planif.tamanioCola)
				free(sectoresCola);
			free(rtaConsola);
			return FIN_ERROR;
		}

		free(rtaConsola);
	}

	/*Informacion al mover cabezal*/
	if (!planif.tamanioCola && !planif.cantSectoresRecorridos)
		cadena = (char *) malloc(12);
	else{
		if (planif.tamanioCola < planif.cantSectoresRecorridos)
			/*len = planif.cantSectoresRecorridos;*/
			cadena = (char *) malloc(11 * planif.cantSectoresRecorridos);
		else
			/*len = planif.tamanioCola;*/
			cadena = (char *) malloc(11 * planif.tamanioCola + 1);
		/*cadena = (char *) malloc(12 * len);*/
	}

	strcpy(cadena, "[");
	for(i = 0; i < planif.tamanioCola; i++)
		sprintf(cadena, "%s%d, ", cadena, *(sectoresCola + i));
	if (planif.tamanioCola)
		free(sectoresCola);
	strcpy(cadena + strlen(cadena) - 2, "]");
	imprimirLog(DETALLE, "Cola Pedidos:", cadena);

	imprimirLogNumerico(DETALLE, "Tamanio de cola de pedidos:", planif.tamanioCola);

	deLogicaAFisica(planif.posicionActual, &direccionFisica);
	sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
	imprimirLog(DETALLE, "Posicion Actual:", cadena);

	deLogicaAFisica(planif.sectorSolicitado, &direccionFisica);
	sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
	imprimirLog(DETALLE, "Sector solicitado:", cadena);

	if (planif.cantSectoresRecorridos){
		strcpy(cadena, "");
		for(i = 0; i < planif.cantSectoresRecorridos; i++){
			deLogicaAFisica(*(sectoresRecorridos + i), &direccionFisica);
			sprintf(cadena, "%s%d:%d, ", cadena, direccionFisica.cilindro, direccionFisica.sector);
		}
		free(sectoresRecorridos);
		*(cadena + strlen(cadena) - 2) = '\0';
	}
	else
		strcpy(cadena, "-");
	imprimirLog(DETALLE, "Sectores Recorridos:", cadena);

	sprintf(cadenaAux, "Tiempo consumido [ms]: %f", planif.tiempoConsumido);
	imprimirLog(DETALLE, cadenaAux, "");

	if (planif.tamanioCola > 1){
		deLogicaAFisica(planif.proximoSector, &direccionFisica);
		sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
	}
	else
		strcpy(cadena, "-");
	imprimirLog(DETALLE, "Proximo sector:", cadena);

	free(cadena);

	return FIN_OK;
}
